package demo1;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 姚东名
 * Date: 2024-06-07
 * Time: 10:48
 */

import java.util.InputMismatchException;
import java.util.Scanner;

/**
 * 异常，其实就是一个一个的类
 * Throwable : Error 和 Exception
 * 1.Error: 指的是Java虚拟机无法解决的严重问题，比如：StackOverflowError和OutOfMemoryError，一旦发生，就无力回天了
 * 2.Exception：异常产生后，程序员可通过代码进行处理。比如：感冒、发烧等
 * 2.1编译时异常：也叫受查异常(Checked Exception)
 * 2.2运行时异常：也叫非受查异常(Unchecked Exception) -> RunTimeException以及其子类对应的异常，都被称为运行时异常。比如：异常。
 * 比如：NullPointerException、ArrayIndexOutOfBoundsException、ArithmeticException。
 * 3.编译时出现的语法错误，不能称之为异常，这是“编译期”出错。而运行指的是程序已经编译通过了，生成了class文件了，再由JVM执行过程中出现的错误
 */
class Person implements Cloneable {
    @Override
    public String toString() {
        return "Person{}";
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

/**
 * 在Java中，异常处理主要的5个关键字：throw、try、catch、finally、throws
 * 1.异常的抛出：在Java中，可以借用throw关键字，抛出一个指定的异常对象，将错误信息告知调用者。
 * 具体语法如下：throw new XXXException("产生异常的原因");
 * 注意事项：1.throw必须写在方法体内部 2.抛出的对象必须是Exception 或者是 Exception 的子类对象
 * 3.如果抛出的是RunTimeException或者是 RunTimeException的子类，则可以不用处理，直接交给JVM（Java虚拟机）处理
 * 4.如果抛出的是编译时异常，用户必须处，否则无法通过编译
 * 5.异常一旦抛出，其后的代码就不会执行了
 * 2.异常的捕获异常的具体处理方式，也就是有两种：异常声明throw以及try-catch捕获处理
 * 2.1异常声明throws：当前方法不处理异常，提醒方法调用者处理异常。
 * 注意事项：1.throws必须跟在方法的参数列表之后  2.声明的异常必须是Exception 或者 Exception的子类
 * 3.方法内部如果抛出多个异常，throws之后必须跟多个异常类型，之间用逗号隔开，如果抛出多个异常类型具有父子类关系，直接声明父类即可。
 * 4.调用声明抛出异常的方法时，调用者必须对该异常进行处理，或者继续使用throws抛出
 * 3.try-catch捕获并处理 -> 详细介绍 -> try{ 可能出现异常的代码 } catch{ 捕获异常 } -> finally{ 处理后事 }
 */
public class Test1 {
    public static void test1() {
        int[] array = {1, 2, 3, 4, 5};
        System.out.println(array[10]);
    }
    public static void main(String[] args) {
        /**
         * 异常处理流程：
         * 关于 "调用栈"
         * 方法之间是存在相互调用关系的, 这种调用关系我们可以用 "调用栈" 来描述. 在 JVM 中有一块内存空间称为
         * "虚拟机栈" 专门存储方法之间的调用关系. 当代码中出现异常的时候, 我们就可以使用 e.printStackTrace(); 的
         * 方式查看出现异常代码的调用栈.
         * 1.如果本方法中没有合适的处理异常的方式，就会沿着调用栈向上传播
         * 2.如果向上传递一直没有合适的方法处理异常，最终就会交给JVM处理，程序就会异常终止(和最开始没有使用try catch时是一样的)
         *
         * [异常处理流程总结]
         * 1.程序最先执行try中的代码
         * 2.如果try中的代码出现异常，就会结束try中的代码，看到和catch中的异常类型是否匹配
         * 3.如果找到匹配的异常类型，就会执行catch中的代码
         * 4.如果没有找到匹配的异常类型，就会将异常向上传递到上层调用者
         * 5.无论是否找到匹配的异常类型，finally中的代码都会被执行到在该方法结束之前执行
         * 6.如果上层调用者也没有处理了异常，就会继续向上传递
         * 7.一直到main方法也没有合适的代码处理异常，就会交给JVM来处理，此时程序就会以异常终止
         */
        try {
            test1();
            System.out.println("结束try的代码...");
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("处理ArrayIndexOutOfBoundsException异常");
        }
        //try-catch语句执行后，还是会执行之后的语句的
        System.out.println("After...");
    }
    public static int testFinally() {
        try {
            return 10;
        } catch (InputMismatchException e) {
            e.printStackTrace();
        } finally {
            System.out.println("finally中的代码");
            return 20;
        }
        //error: return 1;//因为finally{} 中的语句一定会执行的，所以一定会return 20，程序结束，return 1一定不会执行
    }
    public static void main9(String[] args) {
        System.out.println(testFinally());
    }
    public static int getData() {
        Scanner scanner = null;
        try {
            scanner = new Scanner(System.in);
            int data = scanner.nextInt();
            return data;
        }catch (InputMismatchException e) {
            e.printStackTrace();
        }finally {
            System.out.println("finally中代码");
            //correct：
            scanner.close();
            //注意：finally中代码一定会执行的，一般在finally进行一些资源清理的扫尾工作
        }
        System.out.println("try-catch-finally之后的代码");
        /*error：if(null != scanner) {
            scanner.close();
        }*/
        /**
         * 上述代码，如果正常输入，成功接受输入后程序就返回了，try-catch-finally之后的代码根本就没有执行。所以输入流就没有被释放，造成资源泄漏
         */
        return 0;
    }
    public static void main8(String[] args) {
        int data = getData();
        System.out.println(data);
    }
    //写法二：
    public static void main7(String[] args) {
        int[] array = {1, 2, 3};
        try {
            //可能出现异常的代码：
            System.out.println(array[20]);
            array = null;
        }catch (ArrayIndexOutOfBoundsException | NullPointerException e) {
            System.out.println("处理ArrayIndexOutOfBoundsException | NullPointerException异常...");
        }finally {
            System.out.println("After");
        }
        /**
         * ArrayIndexOutOfBoundsException | NullPointerException e
         * 这样虽然减少了代码量，但是也 让代码的可读性降低了，即使执行了，也不知是哪个异常出现错误了
         */
    }
    public static void main6(String[] args) {
        int[] array = {1, 2, 3, 4};
        try {
            //可能出现异常的代码：
            System.out.println(array[20]);
            array = null;
            System.out.println(array.length);
        }catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("处理ArrayIndexOutOfBoundsException异常...");
        }catch (Exception e) {
            System.out.println("处理Exception异常...");
        }finally {
            //finally中的代码一定会执行的，一般在finally中进行一些资源清理的扫尾工作。
            System.out.println("finally被执行...[不管是否发生异常 finally都会执行]");
            System.out.println("After");
        }
        System.out.println("After");
    }
    //写法一：
    public static void main5(String[] args) {
        int[] array = {1, 2, 3};
        try {
            //可能出现异常的代码：
            System.out.println(array[20]);
            array = null;
        }catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("处理ArrayIndexOutOfBoundsException异常...");
        }catch (NullPointerException e) {
            System.out.println("处理NullPointerException异常...");
        }finally {
            System.out.println("After");
        }
        /**
         * 问题：为什么catch (NullPointerException e)语句不执行？（会不会同时抛出多个异常？）
         * 答：不会抛出多个异常，因为ArrayIndexOutOfBoundsException异常一旦抛出，其后的代码就不会执行了，所以后面的异常自然也不会执行
         */
    }

    //抛出异常：
    public static void func(int a) throws CloneNotSupportedException {
        if (a == 10) {            //声明异常：当前方法可能会抛出异常 -> 但是这里的异常并没有被处理，只是交给JVM处理，一旦交给JVM处理，程序就立即中止了
            //抛出异常：           //但是会从当前方法开始检查，是否会处理异常，直到走到main方法，假如main方法还是没有对异常进行处理，就会交给JVM，终止程序了
            throw new CloneNotSupportedException();
        }
    }

    public static void main4(String[] args) {
        try {
            //可能出现异常的代码：
            func(10);
        } catch (CloneNotSupportedException e) {
            //可捕获到的异常：
            System.out.println("捕获到异常，处理异常...");
        } finally {
            //在程序正常或者异常退出时，必须要对资源进进行回收,finally{} 中的代码一定会执行
            System.out.println("程序正常进行...");
        }
    }

    public static void testException(int a) {
        if (a == 0) {
            throw new ArithmeticException("a == 0");
            //异常一旦抛出，其后的代码就不会执行了：
            //error：System.out.println("abcdef");
        }
    }

    public static void main3(String[] args) {
        testException(0);
    }

    public static void main2(String[] args) throws CloneNotSupportedException {
        //声明异常：当前方法可能会抛出异常 -> 但是这里的异常并没有被处理，只是交给JVM处理，一旦交给JVM处理，程序就立即中止了
        Person person = new Person();
        /**1.向下转型：clone方法的类型是Object，person对象的类型是Person，需进行向下转型
         2.protected限定词修饰 -> 只能在同一个包或不同包的子类（具有继承的性质）中使用
         3.抛出异常 -> throws CloneNotSupportedException*/
        Person person1 = (Person) person.clone();
        System.out.println(person1);
    }

    public static void main1(String[] args) {
        //介绍异常：
        /*//1.ArithmeticException -> 算数异常
        System.out.println(10 / 0);

        //2.ArrayIndexOutOfBoundsException -> 数组越界异常
        int[] array = {1, 2, 3, 4, 5};
        System.out.println(array[10]);

        //3.NullPointerException -> 空指针异常
        int[] array = null;
        System.out.println(array.length);*/
    }
}
